package com.yyj.ws;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yyj.pojo.Message;
import com.yyj.util.MessageUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Component：代表当前类可以被Spring管理；
 * @ServerEndpoint：代表当前对象可以被WebSocketConfig中创建的ServerEndpointExporter对象扫描到，然后就可以发挥WebSocket的真正功能 configurator = GetHttpSessionConfigurator.class：使WebSocket中的ServerEndpointConfig对象和HttpSession建立联系
 */
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {

    private final static Logger LOGGER = LogManager.getLogger(ChatEndpoint.class);

    /**
     * 存储在线用户，由当前类所有，所以用static修饰；key：用户名称；value：对应用户的ChatEndpoint对象
     */
    private static Map<String, ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();

    /**
     * 给客户端发送消息的session
     */
    private Session session;

    /**
     * 会话HttpSession对象，存放着当前会话中用户的基本信息，在登录成功后，可以获取HttpSession域中存放的username
     */
    private HttpSession httpSession;

    /**
     * 建立时调用
     *
     * @param session        发送消息的session
     * @param endpointConfig 存储着当前会话的HttpSession对象
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) {
        LOGGER.info("onOpen……");
        // 每次连接建立的时候，将session对象放在ChatEndpoint对象中，到时候我们可以取出来向客户端发送消息，其中session对象可以向客户端发送消息
        this.session = session;
        // 获取HttpSession会话对象，我们可以通过它获取当前用户信息
        HttpSession httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;
        //获取HttpSession域中存放的username
        String username = (String) httpSession.getAttribute("username");
        // 将当前登录用户信息和对应的ChatEndpoint对象存放到onlineUsers中
        onlineUsers.put(username, this);
        // 封装系统推送消息，前端onmessage事件可以接收到该数据
        String message = MessageUtils.getMessage(true, null, getAllOnlineUsername());
        // 通过系统消息将所有在线用户推送给客户端
        broadcastMsgToAllOnlineUsers(message);
    }

    /**
     * 将目前在线的用户列表告诉所有的在线用户
     *
     * @param message 给客户端发送的消息
     * @return
     */
    private void broadcastMsgToAllOnlineUsers(String message) {
        //所有登录用户名称
        Set<String> names = onlineUsers.keySet();
        for (String name : names) {
            // 存放着可以向客户端发送消息的session对象
            ChatEndpoint chatEndpoint = onlineUsers.get(name);
            // 推送消息到对应客户端
            RemoteEndpoint.Basic basicRemote = chatEndpoint.session.getBasicRemote();
            try {
                basicRemote.sendText(message);
            } catch (IOException e) {
                LOGGER.error("广播发送系统消息失败！{}", e);
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取所有在线用户集合
     *
     * @return
     */
    private Set<String> getAllOnlineUsername() {
        return ChatEndpoint.onlineUsers.keySet();
    }


    /**
     * 接收到客户端发送的数据时调用
     *
     * @param message 客户端发送的数据
     * @param session session对象
     * @return void
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        LOGGER.info("onMessage……");
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            Message msg = objectMapper.readValue(message, Message.class);
            //获取接收信息的用户
            String toName = msg.getToName();
            //获取发送的消息
            String msgData = msg.getMessage();
            //获取当前登录的用户
            String username = (String) httpSession.getAttribute("username");
            //封装发送的消息
            String sendMsg = MessageUtils.getMessage(false, username, msgData);
            //发送消息
            onlineUsers.get(toName).session.getBasicRemote().sendText(sendMsg);
        } catch (JsonProcessingException e) {
            LOGGER.error("接收客户端的消息，转换出错了！");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 关闭时调用
     */
    @OnClose
    public void onClose(Session session) {
        LOGGER.info("onClose……");
        String username = (String) httpSession.getAttribute("username");
        // 将离线用户从在线用户列表中移除
        onlineUsers.remove(username);
        // 全局广播目前在线的用户
        String message = MessageUtils.getMessage(true, null, getAllOnlineUsername());
        broadcastMsgToAllOnlineUsers(message);
    }

}
